#!/bin/bash

set -x
set -e
set -u
set +f

# Think that script is being run on ABF
ABF="${ABF:-0}"

# ABF searches for results here
RESULTS_DIR="${RESULTS_DIR:-/home/vagrant/results}"

# Number of iso build.	Example: BUILD_ID=alpha_$BUILD_ID
BUILD_ID="${BUILD_ID:-XXX}"

# Name of platform.	Example: PLATFORM=rosa2023.1
PLATFORM="${PLATFORM:-rosa2021.1}"
# Platfom version for iso, e.g. 2021.1
PLAT_ISO="$(echo "$PLATFORM" | tr -d '[[:alpha:]]')"
# mdvver, e.g. 202110 or 202105
MDVVER="$(echo "$PLATFORM" | tr -d '[[:alpha:]]' | tr -d '.')"
if [ ${#MDVVER} -lt 6 ]; then
	# add 0
	MDVVER="${MDVVER}0"
fi

# Name of DE.		Example: DE=Plasma5
DE="${DE:-Gnome}"
DE="$(echo "$DE" | tr '[[:lower:]]' '[[:upper:]]')"
de="$(echo "$DE" | tr '[[:upper:]]' '[[:lower:]]')"

# Architecture CPU.	Example: ARCH=x86_64
ARCH="${ARCH:-x86_64}"
ARCH="$(echo "$ARCH" | tr '[[:upper:]]' '[[:lower:]]')"

# Force drop packages.	Example: FILTER_PACKAGES="name1 name2 name3"
FILTER_PACKAGES="${FILTER_PACKAGES:-0}"

# Force add packages.	Example: ADD_PACKAGES="name1 name2 name3"
ADD_PACKAGES="${ADD_PACKAGES:-0}"

# Add repos
# ABF allows to attach containers from other build lists as repositories, we will call them "dependencies".
# Examples:
# 1) "555444" will add a container from https://abf.io/build_lists/555444 and container dependencies
# 2) "-555444" will add a container from https://abf.io/build_lists/555444 (without resolving container dependencies)
# 3) "555444 555333" will do (1) for 555444 and 555333
# 4) "+555,401,402,403" will add containers from build lists 555401, 555402, 555403 and their dependencies
# 5) "+-555,401,402,403" will add containers from build lists 555401, 555402, 555403 without dependencies
# 6) "%repo" will add https://abf-downloads.rosalinux.ru/%repo_personal/repository/rosa2021.1/$ARCH/main/release/
# 7) "https://my.repo/x" or "http://my.repo/x" will add the provided URL as a repo
# 8) "/mnt/repo/x" will add the provided local path as a repo
ADD_REPOS="${ADD_REPOS:-0}"

# Add repos listed in $ADD_REPOS as dnf repositiories inside the resulting ISO?
# 1 — yes, 0 — no
ADD_REPOS_INTO_ISO="${ADD_REPOS_INTO_ISO:-0}"

# set to 0 to build ISOs without contrib repo
ENABLE_CONTRIB_REPOS="${ENABLE_CONTRIB_REPOS:-1}"

# set to 1 to build ISOs without testing repos
ENABLE_TESTING_REPOS="${ENABLE_TESTING_REPOS:-0}"

# Add i686 repos when building an x86_64 ISO
ENABLE_32BIT_REPOS="${ENABLE_32BIT_REPOS:-1}"

# set to 1 to disable all standard repos
# (useful e.g. when building from a local mirror,
# where all repos will be added separately)
DISABLE_STANDARD_REPOS="${DISABLE_STANDARD_REPOS:-0}"

# Easily change repository mirror. Examples:
# REPO_BASE_URL=http://abf-downloads.rosalinux.ru (defaults)
# REPO_BASE_URL=/mnt/mirror (local mirror)
# REPO_BASE_URL=http://mirror.rosalinux.ru/rosa (ROSA mirror)
# REPO_BASE_URL=http://mirror.yandex.ru/rosa (Yandex mirror)
# REPO_BASE_URL=http://repo.os.mos.ru (MOS mirror)
REPO_BASE_URL="${REPO_BASE_URL:-http://abf-downloads.rosalinux.ru}"

# set to 1 to force add "Recommends:" from task-iso and their tasks to packages list.
# Example: FORCE_RECOMMENDS=1
FORCE_RECOMMENDS="${FORCE_RECOMMENDS:-0}"

# fresh, chrome
BRANDING="${BRANDING:-fresh}"

# ROSA, MOS (prefix for ISO name)
DISTRO="${DISTRO:-ROSA}"

# run dnf distro-sync or not (useful to off it during local builds)
DISTROSYNC="${DISTROSYNC:-1}"

# Set to 0 to save temporary directories for debugging
CLEANUP="${CLEANUP:-1}"

# Which customization/features/* to add
# Examples:
# CUSTOMIZATION_FEATURES="samba-ad" to build a LiveCD with a Samba AD server
# ("ssh-server"will be added automatically as a dependency of "samba-ad")
# CUSTOMIZATION_FEATURES"xxx yyy"
CUSTOMIZATION_FEATURES="${CUSTOMIZATION_FEATURES:-0}"

# Allow to pass extra args like location of cache etc.
# E.g.: LIVECD_CREATOR_EXTRA_ARGS="--cache=/var/tmp/livecd-creator-cache"
LIVECD_CREATOR_EXTRA_ARGS="${LIVECD_CREATOR_EXTRA_ARGS:-}"

# Allow to set custom compression of squashfs, e.g.:
# LIVECD_CREATOR_COMPRESSION="xz"
# Code of livecd-creator is such that we can add any additional args to mksquashfs here.
# Usage of zstd gives an a bit larger size of the resulting ISO compared to xz,
# but start up and installation (rsync by Anaconda) are much faster.
LIVECD_CREATOR_COMPRESSION="${LIVECD_CREATOR_COMPRESSION:-zstd -Xcompression-level 18}"

# run livecd building under strace for debugging
STRACE="${STRACE:-0}"

# wait before starting the build (wait e.g. for publishing of a package to finish)
SLEEP="${SLEEP:-0}"

# Setup tmpfs (RAM disk) and run the build in it
TMPFS="${TMPFS:-0}"

# size of tmpfs
TMPFS_SIZE="${TMPFS_SIZE:-30G}"

# TMPFS is directory for temporary files
# Set TMPFS=/xxx if you need a custom root for temporary files
# Resulting ISO will also be here before copying into $RESULTS_DIR
if [ "$TMPFS" = 1 ]
	then TMPDIR="${TMPDIR:-/tmp}"
	else TMPDIR="${TMPDIR:-/var/tmp}"
fi
_TMPDIR="$(mktemp --tmpdir="$TMPDIR" -d)"
trap 'if [ "$CLEANUP" = 1 ]; then rm -fr "$_TMPDIR"; fi' EXIT

# Set LC_ALL=C.UTF-8 or use current locale
FORCE_C_LOCALE="${FORCE_C_LOCALE:-1}"
###

# Additional options for dnf install ${deps}
# it may be list of packages for build environment or/and additional repo:
# DNF_IN_OPTS="--nogpgcheck --repofrompath abf-user,http://abf-downloads.rosalinux.ru/abf-user_personal/repository/rosa2021.1/x86_64/main/release/"
DNF_IN_OPTS="${DNF_IN_OPTS:-}"
###

# Text for rosa-iso-info %description tag
COMMENT="${COMMENT:-Information about ISO} \\
Additional repos: $ADD_REPOS \\
Extra args for livecd-creator: $LIVECD_CREATOR_EXTRA_ARGS \\
Customization features: $CUSTOMIZATION_FEATURES \\
Filter for packages: $FILTER_PACKAGES \\
Enable testing repos: $ENABLE_TESTING_REPOS"
###

# If $FS_LABEL was set manually
if [ -n "${FS_LABEL:-}" ]
then
	if [ ${#FS_LABEL} -gt 32 ]; then
		echo "FS_LABEL must be <= 32 characters!"
		exit 1
	fi
	FS_LABEL_ISO="$FS_LABEL"
# IF $FS_LABEL was not set, generate it automatically
else
	FS_LABEL="${DISTRO}_${PLAT_ISO}_${DE}_${ARCH}_${BUILD_ID}"
	FS_LABEL_ISO="$FS_LABEL"
	if [ ${#FS_LABEL_ISO} -gt 32 ]; then
		# try without "${PLAT_ISO}"
		FS_LABEL_ISO="${DISTRO}_${DE}_${ARCH}_${BUILD_ID}"
		if [ ${#FS_LABEL_ISO} -gt 32 ]; then
			FS_LABEL_ISO="${BUILD_ID}"
		fi
		if [ ${#FS_LABEL_ISO} -gt 32 ]; then
			echo "Could not generate FS_LABEL of <= 32 characters!"
			exit 1
		fi
	fi
fi

#### functions
# shellcheck disable=SC2120
_mktemp(){
	mktemp --tmpdir="$_TMPDIR" "$@"
}
#### /functions

if [ ! -d DEs/"$de" ]; then
	echo "DE $de does not exist"
	exit 1
fi

sleep "$SLEEP"

# Adjust rosa-repos* to avoid waiting for mirrors to sync
if [ "$ABF" = 1 ]; then
	sed -i'' -e 's,mirror.rosalinux.ru/rosa,abf-downloads.rosalinux.ru,g' /etc/yum.repos.d/*
fi

if [ "$DISTROSYNC" = 1 ]; then
	dnf --refresh -y distro-sync
	# restore previously changed repos if rosa-repos* packages were distro-synced
	if [ "$ABF" = 1 ]; then
		sed -i'' -e 's,mirror.rosalinux.ru/rosa,abf-downloads.rosalinux.ru,g' /etc/yum.repos.d/*
	fi
fi

# keep this list in sync with customizations/features/iso-builder/packages.ks
# also needed for customizations/features/repo-in-iso
deps="
coreutils
locales-en
git-core
grep
sed
livecd-tools
php-cli
php-curl
rpm-build
xz
gdisk
createrepo_c
"

STRACE_CMD=""
if [ "$STRACE" = 1 ]; then
	deps="$deps strace"
	STRACE_CMD="strace -f -r -o ${RESULTS_DIR}/strace_${BUILD_ID}.log"
fi

# shellcheck disable=SC2086
if ! rpm -q --whatprovides ${deps} ; then
	dnf --refresh -y install ${deps} $DNF_IN_OPTS
fi

# Scriptlets of RPM packages installed into the ISO use system locale;
# ensure consistent results by forcing one known locale
if [ "$FORCE_C_LOCALE" = 1 ]; then
	for i in $(env | grep ^LC_ | awk -F '=' '{print $1}')
	do
		unset "$i"
	done
	unset LANG
	export LC_ALL=C.UTF-8
fi

cp -r DEs customizations packages repos "$_TMPDIR"
if [ "$ARCH" = x86_64 ] && [ "$ENABLE_32BIT_REPOS" = 1 ]; then
	cat repos/repos-i686.ks >> "$_TMPDIR"/repos/repos-"$ARCH".ks
fi
pushd "$_TMPDIR"
	git init
	git config user.email iso-builder@localhost.tld
	git config user.name iso-builder
	git add .
	git commit -m 'Init (copied sources)'
popd

# Set repo paths
sed -i'' -e "s,@PLATFORM@,$PLATFORM,g" -e "s,@REPO_BASE_URL@,$REPO_BASE_URL,g" "$_TMPDIR"/repos/repos-"$ARCH".ks

if [ "$ENABLE_CONTRIB_REPOS" = 0 ]; then
	sed -i'' -e '/\/contrib\//d' "$_TMPDIR"/repos/repos-"$ARCH".ks
fi

if [ "$ENABLE_TESTING_REPOS" = 0 ]; then
	sed -i'' -e '/\/testing/d' "$_TMPDIR"/repos/repos-"$ARCH".ks
fi

if [ "$DISABLE_STANDARD_REPOS" = 1 ]; then
	echo > "$_TMPDIR"/repos/repos-"$ARCH".ks
fi

case "$ARCH" in
	aarch64 | e2k* )
		sed -i'' "$_TMPDIR"/packages/*.ks \
			 -e '/^syslinux/d'  \
			 -e '/^pcmemtest/d' \
			 -e '/^virtualbox/d'
	;;
esac

if [ "$BRANDING" != "fresh" ]; then
	sed -i'' -e "s,^branding-configs-fresh,branding-configs-${BRANDING}," "$_TMPDIR"/packages/pkgs-*.ks
fi

add_pkgs="$(_mktemp)"
add_repos="$(_mktemp)"
add_repos_into_iso="$(_mktemp)"

if [ "$FORCE_RECOMMENDS" = "1" ]; then
	frtemp="$(_mktemp)"
	frtemp_end="$(_mktemp)"
	dnf rq --recommends --resolve "task-iso-$de" >> "$frtemp"
	dnf rq --recommends --resolve "task-iso-$de-var" >> "$frtemp"
	dnf rq --recommends --resolve "task-iso-common" >> "$frtemp"
	dnf rq --recommends --resolve "task-iso-common-var" >> "$frtemp"
	cat "$frtemp" |grep "$ARCH$\|noarch$" |sort -u > "$frtemp_end"
	# read recommends from recommends tasks like task-lxqt or task-x11
	cat "$frtemp_end" |grep "^task-" |grep -v "task-iso-*" > "$frtemp"
	while read -r LINE; do
		dnf rq --recommends --resolve "$LINE" >> "$frtemp_end"
	done < "$frtemp"
	cat "$frtemp_end" |grep "$ARCH$\|noarch$" |rev |cut -d- -f3- |rev |sort -u > "$frtemp"
	cp -f "$frtemp" "$frtemp_end"
	# drop filtered packages
	if ! [ "$FILTER_PACKAGES" = "0" ]; then
		fr_fp="$(_mktemp)"
		# &\n& for guarantee drop by uniq -u
		echo "$FILTER_PACKAGES" |sed "s/^\| \|,\|;/\n/g" |sed "s/.*/&\n&/" > "$frtemp"
		cat "$frtemp_end" >> "$frtemp"
		cat "$frtemp" |sort |uniq -u > "$frtemp_end"
	fi
	cat "$frtemp_end" >> "$add_pkgs"
	FS_LABEL="${DISTRO}_TEST_FORCE_REC_${BUILD_ID}"
fi

# shellcheck disable=SC2001
if ! [ "$ADD_PACKAGES" = "0" ]; then echo "$ADD_PACKAGES" |sed "s/^\| \|,\|;/\n/g" >> "$add_pkgs"; fi
# shellcheck disable=SC2001
if ! [ "$FILTER_PACKAGES" = "0" ]; then echo "$FILTER_PACKAGES" |sed "s/^\| \|,\|;/\n-/g" >> "$add_pkgs"; fi

# $1: path to $add_repos
# $2: repo name
# $3: repo URL
_add_repo_to_ks(){
	echo "repo --name=${2} --baseurl=${3}" >> "$1"
}

# $1: path to $add_repos_into_iso
# $2: repo name
# $3: repo URL
_add_repo_to_iso(){
	cat >> "$1" << EOF
cat > /etc/yum.repos.d/custom_${2}.repo << 'END'
[$2]
name=$2
baseurl=$3
enabled=1
gpgcheck=0
END
EOF
}

# $1: build list ID
# $2: path to $add_repos
# $3: add repos into the resulting ISO (1 - yes, 0 - no)
# $4: path to $add_repos_into_iso
_process_buildlist(){
	local repo_name
	local repo_url
	if [ ${1:0:1} = "-" ]; then
		repo_name="${1:1}"
		repo_url="http://abf-downloads.rosalinux.ru/${PLATFORM}/container/${1:1}/${ARCH}/main/release/"
		# if container number starts with "-", try to guess the URL without using ABF API and without resolving chain of containers
		_add_repo_to_ks "$2" "$repo_name" "$repo_url"
		if [ "$3" = 1 ]; then
			_add_repo_to_iso "$4" "$repo_name" "$repo_url"
		fi
		return
	fi
	if ! urls="$(./abf-api.php gc "$1")"; then
		echo "Error getting containers by abf-api.php!"
		return 1
	fi
	while read -r line2
	do
		# skip empty lines
		if [ -z "$line2" ]; then continue; fi
		repo_name="$(head -c55 /dev/urandom | md5sum | awk '{print $1}')"
		repo_url="$line2"
		_add_repo_to_ks "$2" "$repo_name" "$repo_url"
		if [ "$3" = 1 ]; then
			_add_repo_to_iso "$4" "$repo_name" "$repo_url"
		fi
	done < <(echo "$urls")
}


if ! [ "$ADD_REPOS" = "0" ]; then
	while read -r line
	do
		urls=""
		repo_name=""
		case "$line" in
			"+"* )
				# if container number starts with "+", then text before "+" is a common part, e.g.:
				# +555,401,402,403 will add 555401, 555402, 555403 and their dependencies
				# +-555,401,402,403 will add 555401, 555402, 555403 without resolving their dependencies
				IFS="," read -a array <<< ${line:1}
				common_part=${array[0]}
				for i in $(seq 1 $((${#array[@]}-1)))
				do
					_process_buildlist "${common_part}${array[$i]}" "$add_repos" "$ADD_REPOS_INTO_ISO" "$add_repos_into_iso"
				done
			;;
			"http://"* | "https://"* | "/"* )
				# use the provided URL (https(s) or a local path) directly
				repo_name="$(md5sum <<< "$line" | awk '{print $1}')"
				echo "repo --name=${repo_name} --baseurl=${line}" >> "$add_repos"
				if [ "$ADD_REPOS_INTO_ISO" = 1 ]; then
					_add_repo_to_iso "$add_repos_into_iso" "$repo_name" "$line"
				fi
			;;
			"%"* )
				# add a personal repository in a short form
				repo_name=${line:1}
				repo_url="https://abf-downloads.rosalinux.ru/${repo_name}_personal/repository/${PLATFORM}/${ARCH}/main/release/"
				echo "repo --name=${repo_name}_personal --baseurl=${repo_url}" >> "$add_repos"
				if [ "$ADD_REPOS_INTO_ISO" = 1 ]; then
					_add_repo_to_iso "$add_repos_into_iso" "$repo_name" "$repo_url"
				fi
			;;
			* )
				_process_buildlist "$line" "$add_repos" "$ADD_REPOS_INTO_ISO" "$add_repos_into_iso"
			;;
		esac
		unset urls
		unset repo_name
	done < <(echo "$ADD_REPOS" | tr ' ' '\n')
fi

RPMBUILD_TMP="$(_mktemp -d)"
rpmbuild \
	--define "_sourcedir $RPMBUILD_TMP" \
	--define "_rpmdir $RPMBUILD_TMP" \
	--define "_build_name_fmt %%{NAME}.rpm" \
	--define "rosa_platform $MDVVER" \
	--define "iso_build_id $BUILD_ID" \
	--define "iso_build_time $(date --utc +%s)" \
	--define "iso_de $de" \
	--define "iso_comment $COMMENT" \
	-bb rosa-iso-info.spec

if [ "$CUSTOMIZATION_FEATURES" != 0 ]; then
	FEATURES_LIST=""
	for i in ${CUSTOMIZATION_FEATURES}
	do
		if [ ! -d customizations/features/"$i" ]; then
			echo "Feature $i does not exist!"
			exit 1
		fi
		# first add dependencies of the feature
		if [ -f customizations/features/"$i"/deps ]; then
			# https://stackoverflow.com/a/16414489
			while read -r line
			do
				if ! [[ "$FEATURES_LIST" =~ (^| )"$line"($| ) ]]; then
					FEATURES_LIST="$FEATURES_LIST $line"
				fi
			done < <(grep -v '#' customizations/features/"$i"/deps | sed '/^[[:space:]]*$/d')
		fi
		# then add the feature itself
		if ! [[ "$FEATURES_LIST" =~ (^| )"$i"($| ) ]]; then
			FEATURES_LIST="$FEATURES_LIST $i"
		fi
	done
	for i in ${FEATURES_LIST}
	do
		while read -r file
		do
			if [ -f customizations/features/"$i"/"$file" ]; then
				cat customizations/features/"$i"/"$file" >> "$_TMPDIR"/customizations/"$file"
			fi
		done < <(set +f && cd customizations && ls -- *.ks | tr ' ' '\n')
	done
fi

# process template
sed template.ks \
	-e "s,@DE@,${de},g" \
	-e "s,@ARCH@,${ARCH},g" \
	-e "s,@ADD_REPOS@,${add_repos},g" \
	-e "s,@ADD_REPOS_INTO_ISO@,${add_repos_into_iso},g" \
	-e "s,@ADD_PACKAGES@,${add_pkgs},g" \
	-e "s,@RESULTS_DIR@,${RESULTS_DIR},g" \
	-e "s,@BUILD_ID@,${BUILD_ID},g" \
	-e "s,@RPMBUILD_TMP@,${RPMBUILD_TMP},g" \
	-e "s,@DIR0@,${PWD},g" \
> "$_TMPDIR"/"$de".ks

# e2kv4, e2kv5 etc.
if [[ "$ARCH" =~ e2k* ]]; then
	# e2k arch does not need a bootloader, it has a internal implementation
	sed -i '/pkgs-bootloaders.ks/d' "$_TMPDIR"/"$de".ks
	# Necessary packages cannot be built yet
	sed -i '/pkgs-vm.ks/d' "$_TMPDIR"/"$de".ks
fi

# avoid an error if the file is not added
touch "$_TMPDIR"/customizations/features/ssh-server/authorized_keys

# Print resulting files into log
cat "$add_repos"
cat "$add_repos_into_iso"
cat "$add_pkgs"
cat "$_TMPDIR"/"$de".ks

if [ "$TMPFS" != 0 ]; then
	tmpfs_path="$(_mktemp -d)"
	mount -t tmpfs -o size="$TMPFS_SIZE" tmpfs "$tmpfs_path"
	mkdir -p "$tmpfs_path"/cache
	mkdir -p "$tmpfs_path"/tmp
	LIVECD_CREATOR_EXTRA_ARGS="--cache=$tmpfs_path/cache --tmpdir=$tmpfs_path/tmp $LIVECD_CREATOR_EXTRA_ARGS"
	# We cannot make /home/vagrant/results a tmpfs in ABF because
	# build is run in docker, tmpfs inside docker will not be accessible from host
	#mkdir -p "$tmpfs_path"/results
	#mkdir -p "$(dirname "$RESULTS_DIR")"
	#ln -s "$tmpfs_path"/results "$RESULTS_DIR"
	trap 'if [ "$CLEANUP" = 1 ]; then umount "$tmpfs_path"; fi' EXIT
fi

mkdir -p "$RESULTS_DIR"

pushd "$_TMPDIR"
	# print any changes made by scripts
	PAGER="" git diff
	# run the build
	# shellcheck disable=SC2086
	time \
	${STRACE_CMD} \
	livecd-creator \
		--verbose \
		--config="$de".ks \
		--compression-type="${LIVECD_CREATOR_COMPRESSION}" \
		${LIVECD_CREATOR_EXTRA_ARGS} \
		--fslabel="$FS_LABEL_ISO"
	mv -v -- *.iso "$RESULTS_DIR"
popd

if [ "$FS_LABEL" != "$FS_LABEL_ISO" ]; then
	mv -v "${RESULTS_DIR}/${FS_LABEL_ISO}.iso" "${RESULTS_DIR}/${FS_LABEL}.iso"
fi

# uncompressed strace log is very big, ~2.5 GiB
if [ "$STRACE" = 1 ]; then
	xz -9 -T0 "${RESULTS_DIR}/strace_${BUILD_ID}.log"
fi

# iso info
gdisk -l "${RESULTS_DIR}/${FS_LABEL}.iso"
xorriso -indev "${RESULTS_DIR}/${FS_LABEL}.iso" -report_system_area plain -report_el_torito plain

echo "DONE: ${RESULTS_DIR}/${FS_LABEL}.iso"
